﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Specifications;

using mvcForum.Web.Extensions;
using mvcForum.Web.Interfaces;
using mvcForum.Web.ViewModels;
using mvcForum.Core.Interfaces.Data;

using SimpleLocalisation;
using SimpleLocalisation.Processing.ParameterValues;
using mvcForum.Core.Configuration;

namespace mvcForum.Web.Helpers {

	public static class ForumHelper {

		/// <summary>
		/// Most of the helper methods for localisation are "stolen" from the Umbraco Jupiter project (with the person of the author).
		/// </summary>
		#region Localisation
		public static String GetLinkText(String key) {
			return ForumHelper.GetLinkText(key, null);
		}

		public static String GetLinkText(String key, Object values) {
			return ForumHelper.GetLinkText(key, values, "mvcForum.Web");
		}

		public static String GetLinkText(String key, Object values, String ns) {
			String output = ForumHelper.GetString(key, values, ns);
			return (String.IsNullOrEmpty(output) ? String.Format("##Missing text, key: {0}, namespace: {1}", key, ns) : output);
		}

		public static String GetLinkText<T>(String key) {
			return ForumHelper.GetLinkText(key, null);
		}

		public static String GetLinkText<T>(String key, Object values) {
			String output = ForumHelper.GetString<T>(key, values);
			return (String.IsNullOrEmpty(output) ? String.Format("##Missing text, key: {0}", key) : output);
		}

		//public static String GetLinkText(String key, Object values, String ns) {
		//    String output = ForumHelper.GetString(key, values, ns);
		//    return (String.IsNullOrEmpty(output) ? String.Format("##Missing text, key: {0}, namespace: {1}", key, ns) : output);
		//}

		public static MvcHtmlString GetHtmlString(String key) {
			return ForumHelper.GetHtmlString(key, null);
		}

		public static MvcHtmlString GetHtmlString(String key, Object values) {
			return ForumHelper.GetHtmlString<mvcForum.Web.ForumConfigurator>(key, values);
		}

		public static MvcHtmlString GetHtmlString(String key, String @namespace, Object values) {
			return new MvcHtmlString(ForumHelper.GetString(key, values, @namespace));
		}

		public static String GetString(String key) {
			return ForumHelper.GetString(key, null);
		}

		public static String GetString(String key, Object[] values) {
			return ForumHelper.GetString<mvcForum.Web.ForumConfigurator>(key, values);
		}

		public static String GetString(String key, Object values) {
			return ForumHelper.GetString<mvcForum.Web.ForumConfigurator>(key, values);
		}

		public static String GetString(String key, Object values, String namespc) {
			TextManager manager = DependencyResolver.Current.GetService<TextManager>();
			return manager.Get(key, values: values, ns: namespc);
		}

		public static String GetString<T>(String key) {
			return GetString<T>(key, null);
		}

		public static String GetString<T>(String key, Object values) {
			TextManager manager = DependencyResolver.Current.GetService<TextManager>();
			//CultureInfo cu = Thread.CurrentThread.CurrentUICulture;
			return manager.Get<T>(key, values: values);
		}

		public static MvcHtmlString GetHtmlString<T>(String text) {
			return new MvcHtmlString(GetString<T>(text));
		}

		public static MvcHtmlString GetHtmlString<T>(String text, Object values) {
			return new MvcHtmlString(GetString<T>(text, values));
		}

		public static FormatWrapper<T> Wrap<T>(this T value, String formatExpression) {
			return new FormatWrapper<T>(value, formatExpression);
		}
		#endregion

		public static MvcHtmlString AttachmentLink(this HtmlHelper html, AttachmentViewModel attachment) {
			TagBuilder tag = new TagBuilder("a");
			String fullPath = String.Format("{0}{1}{2}", attachment.Path, attachment.Path.EndsWith("/") ? "" : "/", attachment.Filename);
			tag.Attributes.Add("href", fullPath);
			tag.SetInnerText(attachment.Filename);
			return MvcHtmlString.Create(tag.ToString() + String.Format(" ({0} kB)", Math.Ceiling((Decimal)attachment.Size / 1024)));
		}

		#region Forum Link
		private static MvcHtmlString ForumLink(int forumId, string forumName, RequestContext request, RouteCollection routeCollection, RouteValueDictionary htmlAttributes) {
			TagBuilder tag = new TagBuilder("a");
			string url = UrlHelper.GenerateUrl("ShowForum", null, null, null, null, null, new RouteValueDictionary { { "id", forumId }, { "title", forumName.ToSlug() } }, routeCollection, request, false);
			tag.MergeAttributes<string, object>(htmlAttributes);
			tag.MergeAttribute("href", url);
			tag.SetInnerText(forumName);
			return MvcHtmlString.Create(tag.ToString());
		}

		public static MvcHtmlString ForumLink(this HtmlHelper html, Forum forum, object htmlAttributes) {
			return ForumLink(forum.Id, forum.Name, html.ViewContext.RequestContext, html.RouteCollection, new RouteValueDictionary(htmlAttributes));
		}

		public static MvcHtmlString ForumLink(this HtmlHelper html, int id, string name, object htmlAttributes) {
			return ForumLink(id, name, html.ViewContext.RequestContext, html.RouteCollection, new RouteValueDictionary(htmlAttributes));
		}
		#endregion

		public static bool Authenticated() {
			//return Factory.Get<IFactory>().Resolve<IWebUserProvider>().Authenticated;
			return HttpContext.Current.User.Identity.IsAuthenticated;
		}

		public static MvcHtmlString GetForumMultiSelect(String name, IList<Int32> selected, IDictionary<String, Object> htmlAttributes) {
			IRepository<Forum> forumRepo = DependencyResolver.Current.GetService<IRepository<Forum>>();

			TagBuilder tag = new TagBuilder("select");
			tag.Attributes.Add("name", name);
			tag.Attributes.Add("multiple", "multiple");
			if (htmlAttributes != null) {
				foreach (KeyValuePair<String, Object> pair in htmlAttributes) {
					tag.Attributes.Add(pair.Key, pair.Value.ToString());
				}
			}

			foreach (Forum forum in forumRepo.ReadAll().ToList()) {
				if (forum.HasAccess(AccessFlag.Read)) {
					TagBuilder option = new TagBuilder("option");
					option.Attributes.Add("value", forum.Id.ToString());
					if (selected.Contains(forum.Id)) {
						option.Attributes.Add("selected", "selected");
					}
					option.SetInnerText(forum.Name);
					tag.InnerHtml += option.ToString();
				}
			}

			return MvcHtmlString.Create(tag.ToString());
		}

		public static IEnumerable<SelectListItem> GetTopicTypes(TopicType type) {
			List<SelectListItem> items = new List<SelectListItem>();
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicType." + TopicType.Regular.ToString()), Value = TopicType.Regular.ToString(), Selected = (type == TopicType.Regular) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicType." + TopicType.Announcement.ToString()), Value = TopicType.Announcement.ToString(), Selected = (type == TopicType.Announcement) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicType." + TopicType.Sticky.ToString()), Value = TopicType.Sticky.ToString(), Selected = (type == TopicType.Sticky) });
			return items;
		}

		public static IEnumerable<SelectListItem> GetAuthorNameOptions(AdminAuthorNameSetting flag) {
			List<SelectListItem> items = new List<SelectListItem>();
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("AdminAuthorNameSetting." + AdminAuthorNameSetting.EmailAddress.ToString()), Value = AdminAuthorNameSetting.EmailAddress.ToString(), Selected = (flag == AdminAuthorNameSetting.EmailAddress) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("AdminAuthorNameSetting." + AdminAuthorNameSetting.FullnameOrUsername.ToString()), Value = AdminAuthorNameSetting.FullnameOrUsername.ToString(), Selected = (flag == AdminAuthorNameSetting.FullnameOrUsername) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("AdminAuthorNameSetting." + AdminAuthorNameSetting.Username.ToString()), Value = AdminAuthorNameSetting.Username.ToString(), Selected = (flag == AdminAuthorNameSetting.Username) });
			// TODO: Include later!
			//items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("AdminAuthorNameSetting." + AdminAuthorNameSetting.UserOption.ToString()), Value = AdminAuthorNameSetting.UserOption.ToString(), Selected = (flag == AdminAuthorNameSetting.UserOption) });
			return items;
		}

		public static IEnumerable<SelectListItem> GetTopicFlags(TopicFlag flag) {
			List<SelectListItem> items = new List<SelectListItem>();
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicFlag." + TopicFlag.Deleted.ToString()), Value = TopicFlag.Deleted.ToString(), Selected = (flag == TopicFlag.Deleted) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicFlag." + TopicFlag.Locked.ToString()), Value = TopicFlag.Locked.ToString(), Selected = (flag == TopicFlag.Locked) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicFlag." + TopicFlag.None.ToString()), Value = TopicFlag.None.ToString(), Selected = (flag == TopicFlag.None) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("TopicFlag." + TopicFlag.Quarantined.ToString()), Value = TopicFlag.Quarantined.ToString(), Selected = (flag == TopicFlag.Quarantined) });
			return items;
		}

		public static IEnumerable<SelectListItem> GetPostFlags(PostFlag flag) {
			List<SelectListItem> items = new List<SelectListItem>();
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("PostFlag." + PostFlag.Deleted.ToString()), Value = PostFlag.Deleted.ToString(), Selected = (flag == PostFlag.Deleted) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("PostFlag." + PostFlag.None.ToString()), Value = PostFlag.None.ToString(), Selected = (flag == PostFlag.None) });
			items.Add(new SelectListItem { Text = ForumHelper.GetString<mvcForum.Web.ForumConfigurator>("PostFlag." + PostFlag.Quarantined.ToString()), Value = PostFlag.Quarantined.ToString(), Selected = (flag == PostFlag.Quarantined) });
			return items;
		}

		public static IEnumerable<SelectListItem> GetContentEditors(String selected) {
			IEnumerable<IContentParser> parsers = DependencyResolver.Current.GetServices<IContentParser>();
			return parsers.Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = (p.Name == selected) });
		}

		public static IEnumerable<SelectListItem> GetGroups() {
			return GetGroups(new String[] { });
		}

		public static IEnumerable<SelectListItem> GetGroups(String[] selected) {
			List<SelectListItem> items = new List<SelectListItem>();
			IRepository<Group> groupRepository = DependencyResolver.Current.GetService<IRepository<Group>>();

			IEnumerable<Group> groups = groupRepository.ReadAll();
			foreach (Group group in groups.OrderBy(x => x.Name)) {
				items.Add(new SelectListItem { Text = group.Name, Value = group.Id.ToString(), Selected = (selected.Contains(group.Id.ToString())) });
			}

			return items;
		}

		public static String GetEditorString() {
			String editor = DependencyResolver.Current.GetService<IConfiguration>().Editor;
			return String.IsNullOrWhiteSpace(editor) ? "Regular" : editor;
		}

		public static String Quote(String author, String content) {
			IConfiguration config = DependencyResolver.Current.GetService<IConfiguration>();
			String output = String.Empty;
			try {
				output = DependencyResolver.Current.GetServices<IContentParser>().Where(p => p.Name == config.Editor).First().Quote(author, content);
			}
			catch { }

			return output;
		}

		public static MvcHtmlString ParseContent(String content) {
			IConfiguration config = DependencyResolver.Current.GetService<IConfiguration>();
			MvcHtmlString output = MvcHtmlString.Empty;
			try {
				output = DependencyResolver.Current.GetServices<IContentParser>().Where(p => p.Name == config.Editor).First().Parse(content);
			}
			catch { }

			return output;
		}

		public static IEnumerable<Forum> GetAccessibleForums() {
			List<Forum> forums = new List<Forum>();

			IRepository<Forum> forumRepo = DependencyResolver.Current.GetService<IRepository<Forum>>();
			foreach (Forum forum in forumRepo.ReadAll().ToList()) {
				if (forum.HasAccess(AccessFlag.Read)) {
					forums.Add(forum);
				}
			}

			return forums;
		}

		private static List<Forum> GetForumsWithAccess(AccessFlag access) {
			List<Forum> output = new List<Forum>();
			IBoardRepository boardrepo = DependencyResolver.Current.GetService<IBoardRepository>();
			IEnumerable<Board> allActiveBoards = boardrepo.ReadManyOptimized(new BoardSpecifications.Enabled());
			// Get the first board in the returned list.
			Board board = allActiveBoards.First();
			foreach (Category cat in board.Categories) {
				foreach (Core.Forum forum in cat.Forums.Where(t => t.ParentForum == null)) {
					GetForumsWithAccess(access, forum, output);
				}
			}

			return output;
		}

		private static void GetForumsWithAccess(AccessFlag access, Core.Forum forum, List<Forum> accessibleForums) {
			if (forum.HasAccess(access)) {
				accessibleForums.Add(forum);
			}
			foreach (Forum subForum in forum.SubForums) {
				GetForumsWithAccess(access, subForum, accessibleForums);
			}
		}

		public static IEnumerable<SelectListItem> GetModerateForums(Int32 forumId) {
			List<Forum> accessibleForums = GetForumsWithAccess(AccessFlag.Moderator);
			return accessibleForums.Where(f => f.Id != forumId).Select(f => new SelectListItem { Text = f.Name, Value = f.Id.ToString() }).OrderBy(i => i.Text);
		}

		public static IEnumerable<SelectListItem> GetAccessMasks(Int32 forumId) {
			List<SelectListItem> items = new List<SelectListItem>();
			IRepository<Forum> forumRepository = DependencyResolver.Current.GetService<IRepository<Forum>>();
			IRepository<AccessMask> masksRepository = DependencyResolver.Current.GetService<IRepository<AccessMask>>();

			var forum = forumRepository.Read(forumId);

			IEnumerable<AccessMask> masks = masksRepository.ReadMany(new AccessMaskSpecifications.SpecificBoard(forum.Category.Board));
			items.Add(new SelectListItem { Text = "(not set)", Value = "" });
			foreach (var mask in masks.OrderBy(x => x.Name)) {
				items.Add(new SelectListItem { Text = mask.Name, Value = mask.Id.ToString() });
			}

			return items;
		}

		public static IEnumerable<SelectListItem> SetSelected(IEnumerable<SelectListItem> items, string selected) {
			var newItems = new List<SelectListItem>();
			foreach (SelectListItem item in items) {
				newItems.Add(new SelectListItem { Text = item.Text, Value = item.Value, Selected = (item.Value == selected) });
			}

			return newItems;
		}

		public static String GetAccessMask(GroupViewModel groupVM, Int32 forumId) {
			IRepository<Group> groupRepository = DependencyResolver.Current.GetService<IRepository<Group>>();
			IRepository<Forum> forumRepository = DependencyResolver.Current.GetService<IRepository<Forum>>();

			var group = groupRepository.Read(groupVM.Id);
			var forum = forumRepository.Read(forumId);

			IRepository<ForumAccess> accessRepository = DependencyResolver.Current.GetService<IRepository<ForumAccess>>();
			ForumAccess forumAccess = accessRepository.ReadOne(new ForumAccessSpecifications.SpecificForumAndGroup(forum, group));

			// TODO: Id ?!?!?
			return (forumAccess == null ? "" : forumAccess.AccessMask.Id.ToString());
		}

		#region Topic Link
		private static MvcHtmlString TopicLink(Int32 id, string title, RequestContext request, RouteCollection routeCollection) {
			TagBuilder tag = new TagBuilder("a");
			string url = UrlHelper.GenerateUrl("ShowTopic", null, null, null, null, null, new RouteValueDictionary { { "id", id }, { "title", title.ToSlug() } }, routeCollection, request, false);
			tag.Attributes.Add("href", url);
			tag.SetInnerText(title);
			return MvcHtmlString.Create(tag.ToString());
		}

		private static MvcHtmlString TopicLink(Int32 id, String title, RequestContext request, RouteCollection routeCollection, RouteValueDictionary htmlAttributes) {
			TagBuilder tag = new TagBuilder("a");
			String url = UrlHelper.GenerateUrl("ShowTopic", null, null, null, null, null, new RouteValueDictionary { { "id", id }, { "title", title.ToSlug() } }, routeCollection, request, false);
			tag.MergeAttributes<String, Object>(htmlAttributes);
			tag.MergeAttribute("href", url);
			tag.SetInnerText(title);
			return MvcHtmlString.Create(tag.ToString());
		}

		public static MvcHtmlString TopicLink(this HtmlHelper html, Int32 id, String title, Object htmlAttributes) {
			return TopicLink(id, title, html.ViewContext.RequestContext, html.RouteCollection, new RouteValueDictionary(htmlAttributes));
		}
		#endregion

		private static Boolean debug = false;
		private static Boolean debugRead = false;
		public static Boolean Debug {
			get {
				if (!debugRead) {
					debug = !String.IsNullOrEmpty(ConfigurationManager.AppSettings["Debug"]) && ConfigurationManager.AppSettings["Debug"] == "true";
					debugRead = true;
				}

				return debug;
			}
		}

		private static Version version = null;
		private static Object versionLock = new Object();
		private static Version Version {
			get {
				if (version == null) {
					lock (versionLock) {
						if (version == null) {
							version = typeof(mvcForum.Core.Forum).Assembly.GetName().Version;
						}
					}
				}
				return version;
			}
		}

		public static String GetVersion() {
			return Version.ToString();
		}

		public static String GetShortVersion() {
			Version v = Version;
			return String.Format("{0}.{1}{2}", v.Major, v.Minor, (v.Build == 0 ? "" : String.Format(".{0}", v.Build)));
		}
	}
}